All files / api blob.ts

93.75% Statements 45/48
50% Branches 3/6
100% Functions 12/12
93.62% Lines 44/47
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143                                2x 2x 2x 2x         2x       37x                   336x                             2x               43x 43x     2x 14x 14x 14x 14x 14x 13x   1x             2x 30x 30x 30x           30x   71x     30x     2x 279x 279x 279x     2x 7x 7x 7x 7x 20x   7x     2x 270x     2x 14x             2x 171x   2x                   2x        
/**
 * Copyright 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
import { PlatformSupport } from '../platform/platform';
import { makeConstructorPrivate } from '../util/api';
import { Code, FirestoreError } from '../util/error';
import {
  invalidClassError,
  validateArgType,
  validateExactNumberOfArgs
} from '../util/input_validation';
import { primitiveComparator } from '../util/misc';
 
/** Helper function to assert Uint8Array is available at runtime. */
function assertUint8ArrayAvailable() {
  Iif (typeof Uint8Array === 'undefined') {
    throw new FirestoreError(
      Code.UNIMPLEMENTED,
      'Uint8Arrays are not available in this environment.'
    );
  }
}
 
/** Helper function to assert Base64 functions are available at runtime. */
function assertBase64Available() {
  Iif (!PlatformSupport.getPlatform().base64Available) {
    throw new FirestoreError(
      Code.UNIMPLEMENTED,
      'Blobs are unavailable in Firestore in this environment.'
    );
  }
}
 
/**
 * Immutable class holding a blob (binary data).
 * This class is directly exposed in the public API.
 *
 * Note that while you can't hide the constructor in JavaScript code, we are
 * using the hack above to make sure no-one outside this module can call it.
 */
export class Blob {
  // Prefix with underscore to signal this is a private variable in JS and
  // prevent it showing up for autocompletion.
  // A binary string is a string with each char as Unicode code point in the
  // range of [0, 255], essentially simulating a byte array.
  private _binaryString: string;
 
  private constructor(binaryString: string) {
    assertBase64Available();
    this._binaryString = binaryString;
  }
 
  static fromBase64String(base64: string): Blob {
    validateExactNumberOfArgs('Blob.fromBase64String', arguments, 1);
    validateArgType('Blob.fromBase64String', 'string', 1, base64);
    assertBase64Available();
    try {
      const binaryString = PlatformSupport.getPlatform().atob(base64);
      return new Blob(binaryString);
    } catch (e) {
      throw new FirestoreError(
        Code.INVALID_ARGUMENT,
        'Failed to construct Blob from Base64 string: ' + e
      );
    }
  }
 
  static fromUint8Array(array: Uint8Array): Blob {
    validateExactNumberOfArgs('Blob.fromUint8Array', arguments, 1);
    assertUint8ArrayAvailable();
    Iif (!(array instanceof Uint8Array)) {
      throw invalidClassError('Blob.fromUint8Array', 'Uint8Array', 1, array);
    }
    // We can't call array.map directly because it expects the return type to
    // be a Uint8Array, whereas we can convert it to a regular array by invoking
    // map on the Array prototype.
    const binaryString = Array.prototype.map
      .call(array, (char: number) => {
        return String.fromCharCode(char);
      })
      .join('');
    return new Blob(binaryString);
  }
 
  toBase64(): string {
    validateExactNumberOfArgs('Blob.toBase64', arguments, 0);
    assertBase64Available();
    return PlatformSupport.getPlatform().btoa(this._binaryString);
  }
 
  toUint8Array(): Uint8Array {
    validateExactNumberOfArgs('Blob.toUint8Array', arguments, 0);
    assertUint8ArrayAvailable();
    const buffer = new Uint8Array(this._binaryString.length);
    for (let i = 0; i < this._binaryString.length; i++) {
      buffer[i] = this._binaryString.charCodeAt(i);
    }
    return buffer;
  }
 
  toString(): string {
    return 'Blob(base64: ' + this.toBase64() + ')';
  }
 
  isEqual(other: Blob): boolean {
    return this._binaryString === other._binaryString;
  }
 
  /**
   * Actually private to JS consumers of our API, so this function is prefixed
   * with an underscore.
   */
  _compareTo(other: Blob): number {
    return primitiveComparator(this._binaryString, other._binaryString);
  }
}
 
// Public instance that disallows construction at runtime. This constructor is
// used when exporting Blob on firebase.firestore.Blob and will be called Blob
// publicly. Internally we still use Blob which has a type checked private
// constructor. Note that Blob and PublicBlob can be used interchangeably in
// instanceof checks.
// For our internal TypeScript code PublicBlob doesn't exist as a type, and so
// we need to use Blob as type and export it too.
// tslint:disable-next-line:variable-name We're treating this as a class name.
export let PublicBlob = makeConstructorPrivate(
  Blob,
  'Use Blob.fromUint8Array() or Blob.fromBase64String() instead.'
);